#include "asio.hpp"
#include <iostream>

using asio::ip::tcp;

class Session : public std::enable_shared_from_this<Session>
{
public:
    enum
    {
        E_SESSION_BUFFER_LEN = 1024,
    };
public:
    Session(tcp::socket socket): socket_(std::move(socket))
    {
    }
    void ReadData()
    {
        auto self(shared_from_this());
        socket_.async_read_some(asio::buffer(buffer_, E_SESSION_BUFFER_LEN),
                                [this, self](std::error_code ec, std::size_t length)
        {
            if(ec)
            {
                std::cerr << "async_read_some error :" << ec.message() << std::endl;
            }
            else
            {
                ReadData();
            }
        });
    }
private:
    tcp::socket socket_;
    std::array<char, E_SESSION_BUFFER_LEN> buffer_;
};

void do_accept(tcp::acceptor & acceptor)
{
    acceptor.async_accept([&acceptor](std::error_code ec, tcp::socket socket)
    {
        if(ec)
        {
            std::cerr << ec.message() << std::endl;
        }
        else
        {
            std::make_shared<Session>(std::move(socket))->ReadData();
            do_accept(acceptor);
        }
    });
}

int main(int argc, char* argv[])
{
    unsigned short port = 8888;
    if(argc > 2)
    {
        port = std::atoi(argv[1]);
    }

    asio::ip::tcp::endpoint local_ep(asio::ip::address_v4::any(), port);

    try
    {
        asio::io_context io_context(1);
        tcp::acceptor acceptor(io_context, local_ep.protocol());
        acceptor.bind(local_ep);
        acceptor.listen();
        do_accept(acceptor);
        io_context.run();
    }
    catch(const std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    return 0;
}